﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Text;

namespace EdgeStatz.ESSystems.Objects
{
    #region Definition Objects
    public class TableDefinition
    {
        #region Instance Variable
        private string name = null;
        private ArrayList columnArray = new ArrayList();
        private int columnCount = 0;
        #endregion

        #region Properties
        public string Name
        {
            get { return name; }
            set { name = value; }
        }
        public ColumnDefinition[] ColumnArray
        {
            get { return (ColumnDefinition[])columnArray.ToArray(typeof(ColumnDefinition)); }
        }
        public int ColumnCount
        {
            get { return columnCount; }
        }

        #endregion

        public TableDefinition(string name)
        {
            this.name = name;
        }

        public void AddColumn(ColumnDefinition column)
        {
            if (column == null)
                throw new NullReferenceException("New column cannot be null");
            columnArray.Add(column);
            columnCount++;
        }

        public bool ColumnExists(string name)
        {
            foreach (ColumnDefinition column in this.ColumnArray)
            {
                if (column.Name.Equals(name))
                    return true;
            }
            return false;
        }

        public string[] GetColumnNames()
        {
            string[] returnArray = new string[columnCount];
            ColumnDefinition[] cd = this.ColumnArray;
            for (int i = 0; i < columnCount; i++)
            {
                returnArray[i] = cd[i].Name;
            }
            return returnArray;
        }

        public ColumnType[] GetColumnTypes()
        {
            ColumnType[] returnArray = new ColumnType[columnCount];
            ColumnDefinition[] cd = this.ColumnArray;
            for (int i = 0; i < columnCount; i++)
            {
                returnArray[i] = cd[i].Type;
            }
            return returnArray;
        }
    }

    public class ColumnDefinition
    {
        #region Instance Variable
        private string name;
        private ColumnType type;
        private bool primaryKey;
        private bool nullable;
        private string defaultValue;
        private ForeignKey foreignKey;
        private int length;
        #endregion

        #region Properties
        public string Name
        {
            get { return name; }
            set { name = value; }
        }
        public ColumnType Type
        {
            get { return type; }
            set { type = value; }
        }
        public bool PrimaryKey
        {
            get { return primaryKey; }
            set { primaryKey = value; }
        }
        public bool Nullable
        {
            get { return nullable; }
            set { nullable = value; }
        }
        public string DefaultValue
        {
            get { return defaultValue; }
            set { defaultValue = value; }
        }
        public ForeignKey ForeignKey
        {
            get { return foreignKey; }
            set { foreignKey = value; }
        }
        public int Length
        {
            get { return length; }
            set { length = value; }
        }
        #endregion

        public ColumnDefinition(string name, ColumnType type, bool primaryKey,
            bool nullable, string defaultValue, ForeignKey foreignKey, int length) 
        {
            this.name = name;
            this.type = type;
            this.primaryKey = primaryKey;
            this.nullable = nullable;
            this.defaultValue = defaultValue;
            this.foreignKey = foreignKey;
            this.length = length;
        }

        public string GetTypeInDBType()
        {
            switch (this.type)
            {
                case ColumnType.GUID:
                    return "uniqueidentifier";
                case ColumnType.Int:
                    return "int";
                case ColumnType.Float:
                    return "float";
                case ColumnType.Bit:
                    return "bit";
                case ColumnType.Datetime:
                    return "datetime";
                case ColumnType.Varchar:
                    return "varchar";
                default:
                    return null;
            }
        }
        public string GetTypeInModelType()
        {
            switch (this.type)
            {
                case ColumnType.GUID:
                    return "GUID";
                case ColumnType.Int:
                    return "int";
                case ColumnType.Float:
                    return "float";
                case ColumnType.Bit:
                    return "bit";
                case ColumnType.Datetime:
                    return "datetime";
                case ColumnType.Varchar:
                    return "varchar";
                default:
                    return null;
            }
        }
        public string GetKeys()
        {
            string returnString = "";

            returnString += PrimaryKey ? "PK" : "";
            returnString += (ForeignKey == null ? "" : (PrimaryKey ? ",FK" : "FK"));
            return returnString;
        }
    }

    public class ForeignKey
    {
        private string foreignKeyTable;
        private string foreignKeyColumn;

        public string Table
        {
            get { return foreignKeyTable; }
            set { foreignKeyTable = value; }
        }

        public string Column
        {
            get { return foreignKeyColumn; }
            set { foreignKeyColumn = value; }
        }

        public ForeignKey(string table, string column)
        {
            this.foreignKeyTable = table;
            this.foreignKeyColumn = column;
        }
        public override string ToString()
        {
            return (foreignKeyTable + "." + foreignKeyColumn);
        }
    }

    public class Provider
    {
        private string name = null;
        private string server = null;
        private string database = null;
        private string uid = null;
        private string pwd = null;
        private bool isDefault = false;

        public string Name
        {
            get { return name; }
            set { name = value; }
        }

        public string Server
        {
            get { return server; }
            set { server = value; }
        }

        public string Database
        {
            get { return database; }
            set { database = value; }
        }

        public string UserId
        {
            get { return uid; }
            set { uid = value; }
        }

        public string Password
        {
            get { return pwd; }
            set { pwd = value; }
        }

        public bool IsDefault
        {
            get { return isDefault; }
            set { isDefault = value; }
        }

        public Provider() { }

        public Provider(string name, string server, string database, string uid, string pwd, bool isdefault)
        {
            this.name = name;
            this.server = server;
            this.database = database;
            this.uid = uid;
            this.pwd = pwd;
            this.isDefault = isdefault;
        }

        public Provider(string name, string server, string database, bool isdefault)
        {
            this.name = name;
            this.server = server;
            this.database = database;
            this.isDefault = isdefault;
        }
        /// <summary>
        /// Format "Name:name; Server:server; Database:database; UserId:uid; Password:pass; IsDefault:bool"
        /// </summary>
        /// <param name="provider"></param>
        public Provider(string provider)
        {
            foreach (string str in provider.Split(new char[] {';'}))
            {
                string str2 = str.Trim();
                string[] split_array = str2.Split(new char[] { ':' });
                if(split_array.Length == 2)
                {
                    switch(split_array[0].ToUpper())
                    {
                        case "NAME":
                            this.name = split_array[1];
                            break;
                        case "SERVER":
                            this.server = split_array[1];
                            break;
                        case "DATABASE":
                            this.database = split_array[1];
                            break;
                        case "USERID":
                            this.uid = split_array[1];
                            break;
                        case "USER ID":
                            goto case "USERID";
                        case "UID":
                            goto case "USERID";
                        case "PASSWORD":
                            this.pwd = split_array[1];
                            break;
                        case "PASS":
                            goto case "PASSWORD";
                        case "PWD":
                            goto case "PASSWORD";
                        case "DEFAULT":
                            this.isDefault = split_array[1].ToLower() == "true" ? true : false;
                            break;
                        case "ISDEFAULT":
                            goto case "DEFAULT";
                    }
                }
            }
        }

        public override string ToString()
        {
            return "Name:" + this.name + ";" + " Server:" + this.server + ";" +
                " Database:" + this.database + ";" + " UserId:" + this.uid + ";" + 
                " Password:" + this.pwd + ";" + " IsDefault:" + isDefault.ToString();
        }
    }
    #endregion

    #region Data Objects
    public class TableData
    {
        private string name;
        private List<RowData> dataRows = new List<RowData> { };
        private List<string> colNames = new List<string> { };
        private List<ColumnType> colTypes = new List<ColumnType> { };
        private int colCount = 0;

        public string Name
        {
            get { return name; }
        }
        public int ColumnCount
        {
            get { return colCount; }
        }
        public string[] ColumnNames
        {
            get { return colNames.ToArray(); }
        }
        public ColumnType[] ColumnTypes
        {
            get { return colTypes.ToArray(); }
        }
        public RowData[] Rows
        {
            get { return dataRows.ToArray(); }
        }
        public TableData(string name)
        {
            this.name = name;
        }
        public TableData(string name, string[] colNames, ColumnType[] colTypes)
        {
            this.name = name;
            if(colNames == null || colNames.Length == 0)
                throw new Exception("Parameter colNames must not be null and must have atleast 1 item.");
            if (colTypes == null || colTypes.Length == 0)
                throw new Exception("Parameter colTypes must not be null and must have atleast 1 item.");
            if (colNames.Length != colTypes.Length)
                throw new Exception("Parameters colTypes and colNames must have the same number of items.");
            this.colNames.AddRange(colNames);
            this.colTypes.AddRange(colTypes);
            this.colCount = this.colNames.Count;
        }
        public void AddColumnDefinition(string value, ColumnType type)
        {
            colNames.Add(value);
            colTypes.Add(type);
            colCount++;
        }
        public void AddRow(object[] items)
        {
            if (items.Length != colCount)
                throw new Exception("The number of items being added must equal the number of rows.");
            dataRows.Add(new RowData(items));
        }
        public void AddRow(RowData items)
        {
            if (items.Count != colCount)
                throw new Exception("The number of items being added must equal the number of rows.");
            dataRows.Add(items);
        }
        public bool RowExists(object[] values)
        {
            foreach(RowData rd in Rows)
            {
                if (rd.Equals(values))
                {
                    return true;
                }
            }
            return false;
        }
        public int ColumnIndex(string name)
        {
            if (name == null)
                return -1;
            for (int i = 0; i < ColumnCount; i++)
                if (ColumnNames[i].Trim().ToLower().Equals(name.Trim().ToLower()))
                    return i;
            return -1;
        }
        public void SetNullRow(int index)
        {
            dataRows[index] = null;
        }
        public void RemoveNullRows()
        {
            dataRows.RemoveAll(NullRow);
        }
        private static bool NullRow(RowData rd)
        {
            if (rd == null)
                return true;
            return false;
        }
        public void ReplaceGuid(Guid existing, Guid newVal)
        {
            foreach (RowData rd in dataRows)
            {
                rd.ReplaceGuid(existing, newVal);
            }
        }
        public object GetItem(int row, int column)
        {
            if (column > colCount + 1 || row > Rows.Length + 1)
                return null;
            return Rows[row].Items[column];
        }
        public object GetItem(int row, string column)
        {
            int i = ColumnIndex(column);
            return i == -1 ? null : GetItem(row, i);
        }
        public Type GetColumnType(int index)
        {
            switch (ColumnTypes[index])
            {
                case ColumnType.GUID:
                    return typeof(Guid);
                case ColumnType.Varchar:
                    return typeof(string);
                case ColumnType.Datetime:
                    return typeof(DateTime);
                case ColumnType.Bit:
                    return typeof(bool);
                case ColumnType.Float:
                    return typeof(float);
                case ColumnType.Int:
                    return typeof(int);
                default:
                    return typeof(string);
            }
        }
    }

    public class RowData
    {
        List<object> data = new List<object> { };
        int count = 0;
        public int Count
        {
            get { return count; }
        }
        public object[] Items
        {
            get { return data.ToArray(); }
        }
        public RowData() { }
        public RowData(object[] array) 
        {
            data.AddRange(array);
            count = data.Count;
        }
        public void Insert(object value, Type type)
        {
            switch (type.ToString().ToLower())
            {
                case "system.string":
                    value = value.ToString();
                    break;
                case "system.guid":
                    value = new Guid(value.ToString());
                    break;
                case "system.datetime":
                    value = DateTime.Parse(value.ToString());
                    break;
                case "system.dbnull":
                    value = DBNull.Value;
                    break;
                case "system.int":
                    value = Convert.ToInt32(value);
                    break;
                case "system.float":
                    value = Convert.ToDecimal(value);
                    break;
                case "system.decimal":
                    value = Convert.ToDecimal(value);
                    break;
                case "system.double":
                    value = Convert.ToDouble(value);
                    break;
                case "system.bit":
                    value = value.Equals("1") ? true : false;
                    break;
                default:
                    break;
            }
            Insert(value);
        }
        public void Insert(object value)
        {
            data.Add(value);
            count = data.Count;
        }
        public object GetItem(int index)
        {
            if (index < count)
                return data[index];
            else
                throw new IndexOutOfRangeException("Index cannot be greater than the number of rows.");
        }
        public bool Equals(object[] obj)
        {
            if (obj.Length != Items.Length)
                return false;
            for (int i = 0; i < obj.Length; i++)
            {
                if (!obj[i].Equals(Items[i]))
                    return false;
            }
            return true;
        }
        public void ReplaceGuid(Guid existing, Guid newVal)
        {
            for (int i = 0; i < data.Count; i++)
            {
                if (data[i].GetType().Equals(typeof(Guid)))
                {
                    if(data[i].Equals(existing))
                        data[i] = newVal;
                }
                else if (data[i].GetType().Equals(typeof(string)))
                {
                    Guid temp;
                    try
                    {
                        temp = new Guid(data[i].ToString());
                    }
                    catch (FormatException ex) { continue; }
                    if (temp.Equals(existing))
                        data[i] = newVal;
                }
            }
        }
    }
    #endregion

    #region Enums
    public enum ColumnType 
    {
        GUID,
        Varchar,
        Int,
        Float,
        Bit,
        Datetime
    }
    
    public enum ModelElement { None, Model, Database, Table, Column, DataRow, Value, Structure, Folder, SubFolder, Enum }
    #endregion
}
